home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Gamers Delight 2
/
Gamers Delight 2.iso
/
Aminet
/
game
/
role
/
pinfocom_3_0.lha
/
Source
/
amiga_game.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-10-22
|
23KB
|
1,044 lines
/* amiga_game.c
*
* ``pinfocom'' -- a portable Infocom Inc. data file interpreter.
* Copyright (C) 1987-1992 InfoTaskForce
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to the
* Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* $Header: RCS/amiga_game.c,v 3.0 1992/10/21 16:56:19 pds Stab $
*/
#ifndef _AMIGA_GLOBAL_H
#include "amiga_global.h"
#endif /* !_AMIGA_GLOBAL_H */
/* GameAdd(struct List *List,int Index,const char *FileName):
*
* Add another game file to the list.
*/
VOID
GameAdd(struct List *List,int Index,const char *FileName)
{
struct Node *Node,
*Next;
Bool GotIt = FALSE;
char *Title = Titles[Index];
/* In order to sort the list properly,
* invert the index counter.
*/
Index = NUM_GAMES - Index;
/* Is this game already in the list? */
Node = List -> lh_Head;
while(Next = Node -> ln_Succ)
{
if(Node -> ln_Pri == Index)
{
GotIt = TRUE;
break;
}
else
Node = Next;
}
/* Is it? */
if(!GotIt)
{
struct GameNode *Node;
int TitleLen;
/* Determine length of game title. */
TitleLen = strlen(Title) + 1;
/* Allocate space for node, title and
* corresponding file name.
*/
if(Node = (struct GameNode *)AllocVec(sizeof(struct GameNode) + TitleLen + strlen(FileName) + 1,MEMF_ANY | MEMF_CLEAR))
{
/* Set up the node contents. */
Node -> gn_Node . ln_Name = (char *)(Node + 1);
Node -> gn_Node . ln_Pri = Index;
Node -> gn_FileName = Node -> gn_Node . ln_Name + TitleLen;
/* Fill in the game title and the
* corresponding file name.
*/
strcpy(Node -> gn_Node . ln_Name, Title);
strcpy(Node -> gn_FileName, FileName);
/* Add the node to the list. */
Enqueue(List,(struct Node *)Node);
}
}
}
/* GameMatch(const char *Name,const char *Pattern):
*
* Test if a name matches a wildcard pattern.
*/
Bool
GameMatch(const char *Name,const char *Pattern)
{
char MatchBuffer[MAX_FILENAME_LENGTH];
/* Tokenize the pattern. */
if(ParsePatternNoCase((STRPTR)Pattern,MatchBuffer,MAX_FILENAME_LENGTH) != -1)
{
/* Make the match. */
if(MatchPatternNoCase(MatchBuffer,(STRPTR)Name))
return(TRUE);
}
return(FALSE);
}
/* GameMultiScan(struct List *GameList,const char *Pattern):
*
* Scan multi-volumne assignment for game files.
*/
VOID
GameMultiScan(struct List *GameList,const char *Pattern)
{
struct DevProc *DevProc = NULL;
struct MsgPort *FileSysTask = GetFileSysTask();
struct FileInfoBlock *FileInfo;
header_t GameHeader;
/* Allocate the fileinfo data. */
if(FileInfo = (struct FileInfoBlock *)AllocDosObjectTags(DOS_FIB,TAG_DONE))
{
/* Loop until all assignments are
* processed.
*/
do
{
/* Get the default filesystem task
* in case we stumble upon NULL
* directory locks.
*/
if(DevProc = GetDeviceProc("Infocom:",DevProc))
{
/* Set the default filesystem task. */
SetFileSysTask(DevProc -> dvp_Port);
/* Check the object type. */
if(Examine(DevProc -> dvp_Lock,FileInfo))
{
/* Is it really a directory? */
if(FileInfo -> fib_DirEntryType > 0)
{
/* Scan the directory... */
while(ExNext(DevProc -> dvp_Lock,FileInfo))
{
/* Did we find a file? */
if(FileInfo -> fib_DirEntryType < 0)
{
/* Does the name match the template? */
if(GameMatch(FileInfo -> fib_FileName,Pattern))
{
/* Build full path name. */
strcpy(TempBuffer,"Infocom:");
if(AddPart(TempBuffer,FileInfo -> fib_FileName,MAX_FILENAME_LENGTH))
{
FILE *GameFile;
/* Try to open the file for reading. */
if(GameFile = fopen(TempBuffer,"rb"))
{
/* Read the game file header. */
if(fread(&GameHeader,sizeof(header_t),1,GameFile) == 1)
{
/* Is it a type 3 game? */
if(GameHeader . z_version == 3)
{
int Serial = 0,
i;
/* Calculate the serial number. */
for(i = 0 ; i < 6 ; i++)
{
Serial *= 10;
Serial += GameHeader . serial_no[i] - '0';
}
/* Try to find a corresponding
* game in the list.
*/
for(i = 0 ; SerialNumbers[i][SERIAL_INDEX] != -1 ; i++)
{
/* Do the serial numbers match? */
if(Serial == SerialNumbers[i][SERIAL_NUMBER] && GameHeader . release == SerialNumbers[i][SERIAL_RELEASE])
{
/* Add the game file to the list. */
GameAdd(GameList,SerialNumbers[i][SERIAL_INDEX],TempBuffer);
break;
}
}
}
}
/* Close the game file. */
fclose(GameFile);
}
}
}
}
}
}
}
}
else
break;
}
while(DevProc && (DevProc -> dvp_Flags & DVPF_ASSIGN));
/* Free the fileinfo data. */
FreeDosObject(DOS_FIB,FileInfo);
}
/* Reset the default filesystem task. */
SetFileSysTask(FileSysTask);
/* Free device process data. */
if(DevProc)
FreeDeviceProc(DevProc);
}
/* GameGetNodeName(struct List *List,int Offset):
*
* Scan the list, returning the nth node.
*/
char *
GameGetNodeName(struct List *List,const int Offset)
{
struct GameNode *Node = (struct GameNode *)List -> lh_Head;
int i;
for(i = 0 ; Node -> gn_Node . ln_Succ && i < Offset ; i++)
Node = (struct GameNode *)Node -> gn_Node . ln_Succ;
return(Node -> gn_FileName);
}
/* GameFreeList(struct List *List):
*
* Free the contents of the list,
* including the list itself.
*/
VOID
GameFreeList(struct List *List)
{
struct Node *Node,
*Next;
Node = List -> lh_Head;
while(Next = Node -> ln_Succ)
{
FreeVec(Node);
Node = Next;
}
FreeVec(List);
}
/* GameIsAssign(const STRPTR Name):
*
* Check to see if a device name passed in
* actually refers to an assignment.
*/
Bool
GameIsAssign(const STRPTR Name)
{
WORD NameLen = strlen(Name) - 1;
Bool Result = FALSE;
/* Does it end with a colon? */
if(Name[NameLen] == ':')
{
struct DosList *DosList;
/* Lock the list of assignments for reading. */
if(DosList = AttemptLockDosList(LDF_ASSIGNS | LDF_READ))
{
STRPTR AssignName;
/* Scan the list... */
while(DosList = NextDosEntry(DosList,LDF_ASSIGNS))
{
/* Convert the name from icky
* BCPL to `C' style string.
*/
AssignName = (STRPTR)BADDR(DosList -> dol_Name);
/* Does the name length match? */
if(AssignName[0] == NameLen)
{
/* Does the name itself match? */
if(!Strnicmp(&AssignName[1],Name,NameLen))
{
Result = TRUE;
break;
}
}
}
/* Unlock the list of assignments. */
UnLockDosList(LDF_ASSIGNS | LDF_READ);
}
}
/* Return the result. */
return(Result);
}
/* GameBuildList(const char *Pattern):
*
* Build a list of infocom games whose names
* match a certain pattern.
*/
struct List *
GameBuildList(const char *Pattern)
{
APTR OldPtr = ThisProcess -> pr_WindowPtr;
struct List *GameList = NULL;
BPTR NewDir;
/* No DOS requesters, please! */
ThisProcess -> pr_WindowPtr = (APTR)-1;
/* Is the assignment present? */
if(NewDir = Lock("Infocom:",ACCESS_READ))
{
/* Allocate space for the new list. */
if(GameList = (struct List *)AllocVec(sizeof(struct List),MEMF_ANY))
{
/* Initialize the list. */
NewList(GameList);
/* Will we have to deal with
* an assignment or a volume?
*/
if(GameIsAssign("Infocom:"))
GameMultiScan(GameList,Pattern);
else
{
struct FileInfoBlock *FileInfo;
header_t GameHeader;
/* Allocate space for two fileinfo blocks. */
if(FileInfo = (struct FileInfoBlock *)AllocDosObjectTags(DOS_FIB,TAG_DONE))
{
/* Take a look at the assignment. */
if(Examine(NewDir,FileInfo))
{
/* Does it really refer to a directory? */
if(FileInfo -> fib_DirEntryType > 0)
{
/* Examine the whole directory. */
while(ExNext(NewDir,FileInfo))
{
/* Is it a file? */
if(FileInfo -> fib_DirEntryType < 0)
{
if(GameMatch(FileInfo -> fib_FileName,Pattern))
{
/* Build a path to the story file,
* if present.
*/
strcpy(TempBuffer,"Infocom:");
if(AddPart(TempBuffer,FileInfo -> fib_FileName,MAX_FILENAME_LENGTH))
{
FILE *GameFile;
/* Try to open the file for reading. */
if(GameFile = fopen(TempBuffer,"rb"))
{
/* Read the game file header. */
if(fread(&GameHeader,sizeof(header_t),1,GameFile) == 1)
{
/* Is it a type 3 game? */
if(GameHeader . z_version == 3)
{
int Serial = 0,
i;
/* Calculate the serial number. */
for(i = 0 ; i < 6 ; i++)
{
Serial *= 10;
Serial += GameHeader . serial_no[i] - '0';
}
/* Try to find a corresponding
* game in the list.
*/
for(i = 0 ; SerialNumbers[i][SERIAL_INDEX] != -1 ; i++)
{
/* Do the serial numbers match? */
if(Serial == SerialNumbers[i][SERIAL_NUMBER] && GameHeader . release == SerialNumbers[i][SERIAL_RELEASE])
{
/* Add the game file to the list. */
GameAdd(GameList,SerialNumbers[i][SERIAL_INDEX],TempBuffer);
break;
}
}
}
}
/* Close the game file. */
fclose(GameFile);
}
}
}
}
}
}
}
/* Free the fileinfo data. */
FreeDosObject(DOS_FIB,FileInfo);
}
}
/* Does the list contain any entries? */
if(!GameList -> lh_Head -> ln_Succ)
{
FreeVec(GameList);
GameList = NULL;
}
}
/* Release the lock on the directory. */
UnLock(NewDir);
}
/* Enable DOS requesters again. */
ThisProcess -> pr_WindowPtr = OldPtr;
/* Return the game file list. */
return(GameList);
}
/* GameGetStoryName():
*
* Ask the user for a story game file name.
*/
char *
GameGetStoryName(const char *Pattern,const struct Window *Window)
{
struct FileRequester *StoryRequest;
char *Result = NULL;
/* Get the current directory name. */
if(!GetCurrentDirName(TempBuffer,MAX_FILENAME_LENGTH))
TempBuffer[0] = 0;
/* Allocate the file requester. */
if(StoryRequest = AllocAslRequestTags(ASL_FileRequest,
ASL_Hail, "Select a story file",
ASL_OKText, "Select",
ASL_File, "Story.Data",
ASL_Dir, TempBuffer,
ASL_Pattern, Pattern,
ASL_FuncFlags, FILF_PATGAD,
Window ? ASL_Window : TAG_IGNORE, Window,
TAG_DONE))
{
/* Loop until a result is found. */
while(!Result)
{
/* Ask the user for a file name. */
if(AslRequestTags(StoryRequest,TAG_DONE))
{
/* Did we get a file name? */
if(StoryRequest -> rf_File[0])
{
/* Copy the directory name. */
strcpy(TempBuffer,StoryRequest -> rf_Dir);
/* Build the full path name. */
if(AddPart(TempBuffer,StoryRequest -> rf_File,MAX_FILENAME_LENGTH))
{
/* Is it a valid story game file? */
if(ConCheckStory(TempBuffer))
Result = TempBuffer;
else
ConShowRequest(Window,"File \"%s\" is not a valid story game file.","Continue",StoryRequest -> rf_File);
}
else
ConShowRequest(Window,"Error building path name!","Continue");
}
else
break;
}
else
break;
}
FreeAslRequest(StoryRequest);
}
return(Result);
}
/* GameCentreWindow():
*
* Adjust coordinates of a window to be opened,
* so that it will come up right under the mouse
* pointer.
*/
VOID
GameCentreWindow(const struct Screen *Screen,const WORD WindowWidth,const WORD WindowHeight,WORD *LeftEdge,WORD *TopEdge)
{
*LeftEdge = Screen -> MouseX - WindowWidth / 2;
*TopEdge = Screen -> MouseY - WindowHeight / 2;
while((*LeftEdge) + WindowWidth > Screen -> Width)
(*LeftEdge)--;
while((*LeftEdge) < 0)
(*LeftEdge)++;
while((*TopEdge) + WindowHeight > Screen -> Height)
(*TopEdge)--;
while((*TopEdge) < 0)
(*TopEdge)++;
}
/* GameCreateGadgets():
*
* Create list view and button for the
* game list selection window.
*/
struct Gadget *
GameCreateGadgets(struct Gadget **GadgetArray,struct Gadget **GadgetList,const APTR VisualInfo,const struct List *Labels,const struct Screen *Screen,WORD *WindowWidth,WORD *WindowHeight)
{
STATIC STRPTR GadgetLabels[] =
{
"Choose a story",
"Select a story file...",
"Cancel"
};
struct Gadget *Gadget;
struct NewGadget NewGadget;
UWORD Counter,
Lines = 0;
struct Node *Node,
*Next;
UWORD MaxWidth,
ListWidth = 0,
ButtonWidth = 0,
Width;
int i;
/* Determine the longest label string. */
Node = Labels -> lh_Head;
while(Next = Node -> ln_Succ)
{
Lines++;
if((Width = TextLength((struct RastPort *)&Screen -> RastPort,Node -> ln_Name,strlen(Node -> ln_Name))) > ListWidth)
ListWidth = Width;
Node = Next;
}
/* Is the button label text longer than
* all the list labels?
*/
if((Width = TextLength((struct RastPort *)&Screen -> RastPort,GadgetLabels[GAMEGAD_LIST],strlen(GadgetLabels[GAMEGAD_LIST]))) > ListWidth)
ListWidth = Width;
/* Determine the longest button label. */
for(i = GAMEGAD_SELECT ; i <= GAMEGAD_CANCEL ; i++)
{
if((Width = TextLength((struct RastPort *)&Screen -> RastPort,GadgetLabels[i],strlen(GadgetLabels[i]))) > ButtonWidth)
ButtonWidth = Width;
}
/* Adjust the values. */
ListWidth = (4 + ListWidth + 4) + 16 + 8;
ButtonWidth = 4 + ButtonWidth + 4;
/* Are the two buttons wider than the list? */
if(2 * ButtonWidth + INTERWIDTH > ListWidth)
MaxWidth = 2 * ButtonWidth + INTERWIDTH;
else
{
MaxWidth = ListWidth;
ButtonWidth = (ListWidth - INTERWIDTH) / 2;
}
/* Loop until the list fits on the screen. */
FOREVER
{
/* Zero the template. */
memset(&NewGadget,0,sizeof(struct NewGadget));
/* Clear the gadget counter. */
Counter = 0;
/* Create gadget list context. */
if(Gadget = CreateContext(GadgetList))
{
/* Set up for gadget creation. */
NewGadget . ng_GadgetText = GadgetLabels[Counter];
NewGadget . ng_GadgetID = Counter;
NewGadget . ng_TextAttr = Screen -> Font;
NewGadget . ng_VisualInfo = VisualInfo;
NewGadget . ng_GadgetID = Counter;
NewGadget . ng_Flags = PLACETEXT_ABOVE;
NewGadget . ng_LeftEdge = Screen -> WBorLeft + INTERWIDTH;
NewGadget . ng_TopEdge = Screen -> WBorTop + Screen -> Font -> ta_YSize + 1 + INTERHEIGHT + Screen -> Font -> ta_YSize + INTERHEIGHT;
NewGadget . ng_Width = MaxWidth;
NewGadget . ng_Height = 4 + Lines * Screen -> Font -> ta_YSize + 4;
/* Create the list view. */
GadgetArray[Counter++] = Gadget = CreateGadget(LISTVIEW_KIND,Gadget,&NewGadget,
GTLV_Labels,Labels,
TAG_DONE);
NewGadget . ng_GadgetText = GadgetLabels[Counter];
NewGadget . ng_GadgetID = Counter;
NewGadget . ng_Flags = NULL;
NewGadget . ng_TopEdge = NewGadget . ng_TopEdge + NewGadget . ng_Height + INTERHEIGHT;
NewGadget . ng_Width = ButtonWidth;
NewGadget . ng_Height = 2 + Screen -> Font -> ta_YSize + 2;
/* Create the left button. */
GadgetArray[Counter++] = Gadget = CreateGadget(BUTTON_KIND,Gadget,&NewGadget,TAG_DONE);
NewGadget . ng_GadgetText = GadgetLabels[Counter];
NewGadget . ng_GadgetID = Counter;
NewGadget . ng_Flags = NULL;
NewGadget . ng_LeftEdge = NewGadget . ng_LeftEdge + NewGadget . ng_Width + INTERWIDTH;
/* Create the right button. */
GadgetArray[Counter] = Gadget = CreateGadget(BUTTON_KIND,Gadget,&NewGadget,TAG_DONE);
/* Adjust window dimensions. */
*WindowWidth = Screen -> WBorLeft + INTERWIDTH + MaxWidth + INTERWIDTH + Screen -> WBorRight;
*WindowHeight = NewGadget . ng_TopEdge + NewGadget . ng_Height + INTERHEIGHT + Screen -> WBorBottom;
/* Is the window larger than the screen? */
if(*WindowHeight > Screen -> Height)
{
/* Free gadget list. */
FreeGadgets(*GadgetList);
*GadgetList = NULL;
/* If possible, reduce the list
* by another line. If this turns
* out to be too much, just fail.
*/
if(Lines > 2)
Lines--;
else
return(NULL);
}
else
return(Gadget);
}
}
}
/* GameSelect(char *Name):
*
* Select a game file either by list or by file requester.
*/
Bool
GameSelect(char *Name)
{
struct Window *Window;
struct List *GameList;
WORD WindowWidth,
WindowHeight;
struct Gadget *GadgetList,
*GadgetArray[GAMEGAD_CANCEL + 1];
Bool Result = FALSE;
struct Screen *Screen;
WORD Left,
Top;
char PatternBuffer[MAX_FILENAME_LENGTH];
int i;
/* Is more than a single filename extension available? */
if(StoryExtensions[1])
{
/* Start with a standard pattern header. */
strcpy(PatternBuffer,"#?(");
/* Add the alternatives. */
for(i = 0 ; StoryExtensions[i] ; i++)
{
/* No extension? Clear the
* pattern and exit.
*/
if(!StoryExtensions[i][0])
{
PatternBuffer[0] = 0;
break;
}
else
{
if(i)
strcat(PatternBuffer,"|");
strcat(PatternBuffer,StoryExtensions[i]);
}
}
/* Finish the pattern. */
if(PatternBuffer[0])
strcat(PatternBuffer,")");
else
strcpy(PatternBuffer,"~(#?.info)");
}
else
{
/* That one's simple: just a standard pattern
* plus the extension.
*/
strcpy(PatternBuffer,"#?");
strcat(PatternBuffer,StoryExtensions[0]);
}
/* Obtain a lock on the default public screen. */
if(Screen = LockPubScreen(NULL))
{
/* Build a list of game files. */
if(GameList = GameBuildList(PatternBuffer))
{
/* Is there only one entry in the list? */
if(!GameList -> lh_Head -> ln_Succ -> ln_Succ)
{
struct GameNode *Node = (struct GameNode *)GameList -> lh_Head;
strcpy(Name,Node -> gn_FileName);
Result = TRUE;
}
else
{
APTR VisualInfo;
/* Obtain visual information. */
if(VisualInfo = GetVisualInfo(Screen,TAG_DONE))
{
/* Create gadget list. */
if(GameCreateGadgets(GadgetArray,&GadgetList,VisualInfo,GameList,Screen,&WindowWidth,&WindowHeight))
{
/* Centre the window under the mouse pointer. */
GameCentreWindow(Screen,WindowWidth,WindowHeight,&Left,&Top);
/* Open the window. */
if(Window = OpenWindowTags(NULL,
WA_Width, WindowWidth,
WA_Height, WindowHeight,
WA_Left, Left,
WA_Top, Top,
WA_Title, "Infocom games",
WA_RMBTrap, TRUE,
WA_DepthGadget, TRUE,
WA_CloseGadget, TRUE,
WA_DragBar, TRUE,
WA_Activate, TRUE,
WA_CustomScreen,Screen,
WA_IDCMP, IDCMP_CLOSEWINDOW | BUTTONIDCMP | LISTVIEWIDCMP,
TAG_DONE))
{
STATIC struct Requester BlockRequest;
struct IntuiMessage *Massage;
ULONG Class,
Code;
struct Gadget *Gadget;
char *Buffer;
Bool Terminated = FALSE;
/* Add and draw the gadget list. */
AddGList(Window,GadgetList,(UWORD)-1,(UWORD)-1,NULL);
RefreshGList(GadgetList,Window,NULL,(UWORD)-1);
GT_RefreshWindow(Window,NULL);
/* Loop, waiting for input. */
do
{
/* Wait for it... */
WaitPort(Window -> UserPort);
/* Process incoming messages. */
while(Massage = GT_GetIMsg(Window -> UserPort))
{
/* Pick up the message data. */
Class = Massage -> Class;
Code = Massage -> Code;
Gadget = (struct Gadget *)Massage -> IAddress;
/* Reply the message to the sender. */
GT_ReplyIMsg(Massage);
/* Process the message class. */
switch(Class)
{
/* Window is to be closed? */
case IDCMP_CLOSEWINDOW: Terminated = TRUE;
break;
/* A button was pressed or a list
* item was selected.
*/
case IDCMP_GADGETUP: switch(Gadget -> GadgetID)
{
/* A list entry was selected,
* use the corresponding game
* file name.
*/
case GAMEGAD_LIST: strcpy(Name,GameGetNodeName(GameList,Code));
Result = Terminated = TRUE;
break;
/* Select a game file. */
case GAMEGAD_SELECT: memset(&BlockRequest,0,sizeof(struct Requester));
/* Block window output. */
Request(&BlockRequest,Window);
/* Set the wait pointer. */
WaitPointer(Window);
/* Ask the user for a file name. */
if(Buffer = GameGetStoryName(PatternBuffer,Window))
{
strcpy(Name,Buffer);
Result = Terminated = TRUE;
}
/* Remove the wait pointer. */
ClearPointer(Window);
/* Remove the blocking requester. */
EndRequest(&BlockRequest,Window);
break;
/* Cancel selection. */
case GAMEGAD_CANCEL: Terminated = TRUE;
break;
}
break;
}
}
}
while(!Terminated);
/* Close the window. */
CloseWindow(Window);
}
/* Free the gadget data. */
FreeGadgets(GadgetList);
}
/* Relese the visual information data. */
FreeVisualInfo(VisualInfo);
}
}
/* Free the game file list. */
GameFreeList(GameList);
}
else
{
char *Buffer;
/* Ask the user for a file name. */
if(Buffer = GameGetStoryName(PatternBuffer,NULL))
{
strcpy(Name,Buffer);
Result = TRUE;
}
}
/* Release the lock on the default
* public screen.
*/
UnlockPubScreen(NULL,Screen);
}
/* Return the result. */
return(Result);
}